﻿using System;
using System.Threading;
using System.Windows.Threading;
using IronCow;
using Milkify.Interfaces;
using System.Windows.Input;

namespace Milkify.Controllers
{
    public abstract class Controller : IController
    {
        public Rtm Rtm { get; private set; }
        public IViewContainer ViewContainer { get; private set; }
        public ControllerContext Context { get; private set; }

        protected Controller(ControllerContext context, Rtm rtm, IViewContainer viewContainer)
        {
            Context = context;
            Rtm = rtm;
            ViewContainer = viewContainer;
        }

        public abstract void Execute();

        #region View Redirection
        protected void Redirect(string controllerName)
        {
            Redirect(controllerName, new ControllerContext());
        }

        protected void Redirect(string controllerName, string commandName)
        {
            Redirect(controllerName, new ControllerContext(commandName));
        }

        protected void Redirect(string controllerName, string commandName, object parameter)
        {
            Redirect(controllerName, new ControllerContext(commandName, parameter));
        }

        protected void Redirect(string controllerName, ControllerContext context)
        {
            IControllerFactory factory = KernelContainer.Kernel.Get<IControllerFactory>();
            IController controller = factory.Create(context, controllerName);
            controller.Execute();
        } 
        #endregion

        #region Asynchronous View Operations
        protected delegate object ViewDataCreator();
        private delegate void ViewDataSetter();
        private class ViewDataCreationParameters
        {
            public IView View;
            public ViewDataCreator Creator;

            public ViewDataCreationParameters(IView view, ViewDataCreator creator)
            {
                View = view;
                Creator = creator;
            }
        }
        protected void AsyncSetViewData(IView view, ViewDataCreator creator)
        {
            Thread thread = new Thread(CreateAndSetViewData);
            thread.IsBackground = true;
            thread.Start(new ViewDataCreationParameters(view, creator));
        }
        private void CreateAndSetViewData(object startData)
        {
            try
            {
                ViewDataCreationParameters parameters = startData as ViewDataCreationParameters;
                if (parameters == null)
                    throw new ArgumentException("The parameter is not a valid ViewDataCreationParameters object.", "startData");

                object viewData = parameters.Creator();
                Action action = () => parameters.View.ViewData = viewData;
                parameters.View.Dispatcher.Invoke(DispatcherPriority.DataBind, action);
            }
            catch (Exception e)
            {
                var exceptionHandler = KernelContainer.Kernel.Get<ApplicationExceptionHandler>();
                exceptionHandler.HandleException(e);
            }
        }

        private class AsyncActionParameters
        {
            public Action Action;

            public AsyncActionParameters(Action action)
            {
                Action = action;
            }
        }
        protected void AsyncAction(Action action)
        {
            Thread thread = new Thread(DoAsyncAction);
            thread.IsBackground = true;
            thread.Start(new AsyncActionParameters(action));
        }
        private void DoAsyncAction(object startData)
        {
            try
            {
                AsyncActionParameters parameters = startData as AsyncActionParameters;
                if (parameters == null)
                    throw new ArgumentException("The parameter is not a valid AsyncActionParameters object.", "startData");

                parameters.Action();
            }
            catch (Exception e)
            {
                var exceptionHandler = KernelContainer.Kernel.Get<ApplicationExceptionHandler>();
                exceptionHandler.HandleException(e);
            }
        }

        protected void DispatchViewAction(IView view, Action action)
        {
            DispatchViewAction(view, view, action);
        }

        protected void DispatchViewAction(IView view, object padlock, Action action)
        {
            lock (padlock)
            {
                view.Dispatcher.Invoke(DispatcherPriority.DataBind, action);
            }
        } 
        #endregion
    }
}
